using System;
using System.IO;
using System.Net.Sockets;
using System.Net;

namespace rudp
{
	public delegate void SentReceiptRequest( CommunicationState state );

	public class MessageSender
	{
		public int				  MtuSize{ get;set; }
		public SentReceiptRequest SendReceiptRequestCallback{ get; set; }
		public int 				  MaxPackets{ get; set; }

		public MessageSender( int mtuSize, int maxPackets )
		{		
			this.MtuSize = mtuSize;
			this.MaxPackets = maxPackets;
		}

		public void Send( byte[] dataToSend, Socket socket, EndPoint remoteHost )
		{
			CommunicationState state = new CommunicationState{
				CurrentStep = Step.DeliveringPackets,
				Packets = MessagePacketManager.Instance.CreateMessagePacketList( dataToSend, this.MaxPackets, this.MtuSize ),
				Socket = socket,
				RemoteHost = remoteHost
			};

			this.Send( state );
		}

		public void Send (CommunicationState state)
		{
			MessagePacket messagePacket = state.Packets [state.Index];
		
			state.Index++;
			state.CurrentStep = Step.DeliveringPackets;

			// Mark the last packet to request a receipt
			if (state.Packets.IsLastMessagePacket (messagePacket)) {
				messagePacket.Flags = MessageFlags.Data | MessageFlags.ReceiptRequest;
				state.CurrentStep = Step.DeliveringReceiptRequest;
			}

			byte[] data = messagePacket.ToByteArray ();

			state.Socket.DontFragment = true;
			state.Socket.UseOnlyOverlappedIO = true;

			IAsyncResult result = state.Socket.BeginSendTo (data, 0, data.Length, SocketFlags.None, state.RemoteHost, this.OnSend, state);
		}

		public void OnSend (IAsyncResult result)
		{		
			CommunicationState state = (CommunicationState)result.AsyncState;

			state.BytesSent = state.Socket.EndSendTo (result);

			if( state.CurrentStep == Step.DeliveringPackets )
				this.Send( state );
			else if( state.CurrentStep == Step.DeliveringReceiptRequest && this.SendReceiptRequestCallback != null )
				this.SendReceiptRequestCallback( state );
		}
	}
}

